/*
 * Albareth - an action roleplaying game.
 * Copyright (C) 2008 Jens Anuth
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *   02110-1301, USA.
 * 
 * $Id: LevelSpawnConfiguration.java 49 2008-05-17 02:35:15Z jens464 $
*/
package albareth.core;

import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import albareth.core.entity.Item;
import albareth.core.entity.ItemFactory;
import albareth.core.entity.Mobile;
import albareth.core.entity.MobileFactory;
import albareth.core.entity.item.BasicItemContainer;
import albareth.core.entity.template.MobileTemplate;
import albareth.frontend.TileCollection;
import frosch.util.FToolkit;
import frosch.util.Range;
import frosch.util.WeightedSet;

public class LevelSpawnConfiguration
{
    /** How many different monster species to appear at most in one dungeon level. */
    public static final int MAX_SPECIES_PER_LEVEL = 5;
    
    private int _dungeonLevel;
    
    /** How crowded the map should become. 1.0 is normal. */
    private double _roomCrowdedRate;

    /** A big boss monster in this level? */
    private boolean _bossLevel = false;

    private WeightedSet _mobileTemplates = new WeightedSet();

    
    public LevelSpawnConfiguration(int dungeonLevel, double crowdedRate, Range levelRange)
    {
        _dungeonLevel = dungeonLevel;
        _roomCrowdedRate = crowdedRate;
        fill(levelRange);
    }
    
    private void fill(Range levelRange)
    {
        MobileFactory factory = MobileFactory.getInstance();
        Vector allTemplates = factory.getAllMonsterTemplates(levelRange);
        for (int i = 0; i < MAX_SPECIES_PER_LEVEL; i++)
        {
            int rnd = FToolkit.rand(allTemplates.size());
            MobileTemplate tpl = (MobileTemplate)allTemplates.get(rnd);
            _mobileTemplates.add(tpl, 1);
        }
    }

    public Mobile getSingleSpawn()
    {
        MobileTemplate mobTpl = (MobileTemplate)_mobileTemplates.randomElement();
        return mobTpl.create();
    }
    
    public Mobile getSingleBossSpawn()
    {
        MobileTemplate mobTpl = (MobileTemplate)_mobileTemplates.randomElement();
        return mobTpl.createBossVariant();
    }
    
    /**
     * Gets the monster spawn for a room with given area.
     */
    public List getRoomMobileSpawn(int roomArea)
    {
        ItemFactory itemFactory = ItemFactory.getInstance();
        
        LinkedList spawnList = new LinkedList();
        double maxFillScore  = (Math.sqrt(roomArea) / 2) * _roomCrowdedRate;
        double currentFillScore = 0.0;
        boolean mixInhabitants = true; //multiple species allowed in this room.

        MobileTemplate mobTpl = (MobileTemplate)_mobileTemplates.randomElement();
        while (currentFillScore < maxFillScore)
        {
            double lootChance = .25; //loot on every fourth monster:
            int lootLevel = _dungeonLevel;
            Mobile mob = mobTpl.create();
            double mobFillScore = ((float)mob.getLevel() / _dungeonLevel) / 2;
            //System.err.println(mobFillScore);
            //room crowded enough to create a boss?
            if (currentFillScore == 0 && maxFillScore / mobFillScore >= 4 &&
                    Math.random() < .33)
            {
                mob = mobTpl.createBossVariant();
                mixInhabitants = false;
                //boss loot 100% and 3 levels higher:
                lootChance = 1;
                lootLevel += 3;
            }
            
            if (Math.random() < lootChance) {
                List monsterLoot = itemFactory.createLootItems(1, lootLevel);
                mob.addAllItems(monsterLoot);
            }
            
            currentFillScore += mobFillScore;
            spawnList.add(mob);
            //next inhabitant from another species?
            if (mixInhabitants && Math.random() < .2) {
                mobTpl = (MobileTemplate)_mobileTemplates.randomElement();
                mixInhabitants = Math.random() < .5; //stop mixing now?
            }
        }
        
        return spawnList;
    }

    /**
     * Gets the item spawn for a room with given area.
     */
    public List getRoomItemSpawn(int roomArea)
    {
        LinkedList spawnList = new LinkedList();
        ItemFactory factory = ItemFactory.getInstance();
        
        //how many items in this room?
        int maxItemsInRoom = (int)Math.round(Math.sqrt(roomArea) / 3);
        int actualItemsInRoom = FToolkit.rand(maxItemsInRoom + 1);

        //barrels or jars in this room? (we don't mix them)
        boolean barrelsOrJars = FToolkit.flipCoin();

        //now roll each item
        for (int i = 0; i < actualItemsInRoom; i++)
        {
            BasicItemContainer container;
            int amount;
            //3:5 chance for destroyable barrel/jar with ONE item in it:
            if (FToolkit.rand(5) < 3) 
            {
                container = barrelsOrJars ?
                        ItemFactory.createDestroyableContainer(
                                "Barrel", TileCollection.BARREL)  :
                        ItemFactory.createDestroyableContainer(
                                "Jar", TileCollection.JAR);
                 amount = 1;
                 
            //2:5 chance for a random chest with MORE items in it:
            } else {
                container = ItemFactory.createRandomContainer();
                amount = FToolkit.rand(1, maxItemsInRoom + 1);
            }
            
            container.addAllItems(
                    factory.createLootItems(amount, _dungeonLevel)
            );
            
            spawnList.add(container);
        }
        
        return spawnList;

    }
    
    
    public void setBossLevel(boolean b)
    {
        _bossLevel = b;
    }
    
    public boolean isBossLevel()
    {
        return _bossLevel;
    }

}
